<template>
  <text ref="aniNumRef">{{ props.format ? props.format(defaultValue) : defaultValue }}</text>
</template>

<script>
  export default {
    name: 'VcAniNumber',
  };
</script>

<script setup>
  import { ref, watch, defineProps, defineEmits, defineExpose, onMounted } from 'vue';
  const props = defineProps({
    // 数值
    value: {
      type: Number,
      default: 0,
    },
    // 初始数值
    initial: {
      type: Number,
      default: 0,
    },
    // 持续时间
    durTime: {
      type: Number,
      default: 3000,
    },
    // 循环动画
    loopAnimate: {
      type: Boolean,
      default: false,
    },
    // 动画
    animate: {
      type: Boolean,
      default: true,
    },
    // 精度
    precision: {
      type: [Number, String],
      default: 0,
    },
    // 步长
    step: {
      type: Number,
      default: 0,
    },
    //上一次动画结束和下一次动画开始之间间隔时间
    delay: {
      type: Number,
      default: 1000,
    },
    // 文本格式化
    format: {
      type: Function,
      default: undefined,
    },
  });

  const emits = defineEmits(['begin', 'finish', 'play']);

  const defaultValue = ref(0);
  const aniNumRef = ref();

  let startTime = performance.now(); // 开始时间
  let finishTime = undefined; // 结束时间
  let aniNum = props.initial; // 初始值

  // 播放动画
  const play = () => {
    const current = performance.now();
    // 设置步长时
    if (props.step) {
      aniNum = aniNum + props.step;
      const minValue = Math.min(aniNum, props.value);
      defaultValue.value = Number(minValue).toFixed(props.precision);
      emits('play', { ref: aniNumRef, value: defaultValue.value });
      if (minValue === props.value) {
        finishTime = current;
        finish();
        return;
      }
    } else {
      const endTime = Math.min(current - startTime, props.durTime);
      const currentValue =
        props.initial + (props.value - props.initial) * (endTime / props.durTime);
      defaultValue.value = Number(currentValue).toFixed(props.precision);
      emits('play', { ref: aniNumRef, value: defaultValue.value });
      if (endTime === props.durTime) {
        finishTime = current;
        finish();
        return;
      }
    }
    requestAnimationFrame(play);
  };

  // 循环
  const loopAni = () => {
    const current = performance.now();
    if (props.step) aniNum = props.initial;
    const interval = Math.min(current - finishTime, props.delay);
    if (interval === props.delay) {
      startTime = current;
      begin();
      return;
    }
    requestAnimationFrame(loopAni);
  };

  // 完成
  const finish = () => {
    if (props.loopAnimate) {
      loopAni();
    }
    emits('finish', aniNumRef);
  };
  // 开始
  const begin = () => {
    emits('begin', aniNumRef);
    startTime = performance.now();
    if (props.step) aniNum = props.initial;
    play();
  };

  defineExpose({
    begin,
    play,
    finish,
    aniNumRef,
  });

  const isAnimate = watch(
    () => props.animate,
    (newValue) => {
      if (newValue) {
        begin();
      }
    }
  );
  onMounted(() => {
    props.animate && begin();
  });
</script>

<style lang="less" scoped></style>
